import { types } from "mobx-state-tree";
import Registry from "../core/Registry";
import Tree from "../core/Tree";
import { AreaMixin } from "../mixins/AreaMixin";
import NormalizationMixin from "../mixins/Normalization";
import RegionsMixin from "../mixins/Regions";
import { RectRegionModel } from "./RectRegion";
import { KeyPointRegionModel } from "./KeyPointRegion";
import { AudioRegionModel } from "./AudioRegion";
import { PolygonRegionModel } from "./PolygonRegion";
import { VectorRegionModel } from "./VectorRegion";
import { EllipseRegionModel } from "./EllipseRegion";
import { RichTextRegionModel } from "./RichTextRegion";
import { BrushRegionModel } from "./BrushRegion";
import { TimelineRegionModel } from "./TimelineRegion";
import { TimeSeriesRegionModel } from "./TimeSeriesRegion";
import { ParagraphsRegionModel } from "./ParagraphsRegion";
import { VideoRectangleRegionModel } from "./VideoRectangleRegion";
import { BitmaskRegionModel } from "./BitmaskRegion";
import { CustomRegionModel } from "./CustomRegion";

// general Area type for classification Results which doesn't belong to any real Area
const ClassificationArea = types.compose(
  "ClassificationArea",
  RegionsMixin,
  NormalizationMixin,
  AreaMixin,
  types
    .model({
      object: types.late(() => types.reference(types.union(...Registry.objectTypes()))),
      // true only for global classifications
      classification: true,
    })
    .views((self) => ({
      get supportSuggestions() {
        return false;
      },
      // it's required in some contexts when it's treated as a region
      get type() {
        return "";
      },
    }))
    .actions(() => ({
      serialize: () => ({}),
    })),
);

const Area = types.union(
  {
    dispatcher(sn) {
      // for some deserializations
      if (sn.$treenode) return sn.$treenode.type;

      for (const customTag of Registry.customTags) {
        if (customTag.region && customTag.detector) {
          if (customTag.detector(sn)) return customTag.region;
        }
      }

      if (
        !sn.points && // dirty hack to make it work with polygons, but may be the whole condition is not necessary at all
        !sn.shape && // same for vector
        // `sequence` and `ranges` are used for video regions
        !sn.sequence &&
        !sn.ranges &&
        !sn.imageDataURL &&
        sn.value &&
        Object.values(sn.value).length <= 1
      )
        return ClassificationArea;
      // may be a tag itself or just its name
      const objectName = Tree.cleanUpId(sn.object.name || sn.object);
      // we have to use current config to detect Object tag by name
      const tag = window.Htx.annotationStore.names.get(objectName);
      // provide value to detect Area by data
      const available = Registry.getAvailableAreas(tag.type, sn);
      // union of all available Areas for this Object type

      // @todo dirty hack to distinguish two video types
      if (tag.type === "video") {
        if (sn.sequence || sn.value?.sequence) return VideoRectangleRegionModel;
        return TimelineRegionModel;
      }

      if (!available.length) return ClassificationArea;
      return types.union(...available, ClassificationArea);
    },
  },
  AudioRegionModel,
  ParagraphsRegionModel,
  TimelineRegionModel,
  TimeSeriesRegionModel,
  RectRegionModel,
  RichTextRegionModel,
  KeyPointRegionModel,
  EllipseRegionModel,
  PolygonRegionModel,
  VectorRegionModel,
  BrushRegionModel,
  BitmaskRegionModel,
  VideoRectangleRegionModel,
  ClassificationArea,
  CustomRegionModel,
  ...Registry.customTags.map((t) => t.region).filter(Boolean),
);

export default Area;
